// Copyright Druid Mechanics


#include "AbilitySystem/Abilities/AuraFireBolt.h"
#include "Kismet/KismetSystemLibrary.h"
#include "AuraGameplayTags.h"
#include "AbilitySystem/AuraAbilitySystemLibrary.h"
#include "Actor/AuraProjectile.h"
#include "GameFramework/ProjectileMovementComponent.h"

FString UAuraFireBolt::GetDescription(int32 Level)
{
	const int32 ScaleDamage = Damage.GetValueAtLevel(Level);
	const float ManaCost = FMath::Abs(GetManaCost(Level));
	const float Cooldown = GetCooldown(Level);
	if (Level == 1)
	{
		return FString::Printf(
			TEXT(
				"<Title>FIRE BOLT</>\n\n"
				"<Small>Level: </><Level>%d</>\n"
				"<Small>ManaCost: </><ManaCost>%.1f</>\n"
				"<Small>Cooldown: </><Cooldown>%.1f</>\n\n"
				"<Default>Launches a bolt of fire, exploding on impact and dealing: </>"
				"<Damage>%d</>"
				"<Default> fire damage with a chance to burn</>"),
			Level, ManaCost, Cooldown, ScaleDamage);
	}
	else
	{
		return FString::Printf(
			TEXT(
				"<Title>FIRE BOLT</>\n\n"
				"<Small>Level: </><Level>%d</>\n"
				"<Small>ManaCost: </><ManaCost>%.1f</>\n"
				"<Small>Cooldown: </><Cooldown>%.1f</>\n\n"
				"<Default>Launches %d bolts of fire, exploding on impact and dealing: </>"
				"<Damage>%d</>"
				"<Default> fire damage with a chance to burn</>"),
			Level, ManaCost, Cooldown, FMath::Min(Level, NumProjectiles), ScaleDamage);
	}
}

FString UAuraFireBolt::GetNextLevelDescription(int32 Level)
{
	const int32 ScaleDamage = Damage.GetValueAtLevel(Level);
	const float ManaCost = FMath::Abs(GetManaCost(Level));
	const float Cooldown = GetCooldown(Level);
	return FString::Printf(
		TEXT(
			"<Title>NEXT LEVEL</>\n\n"
			"<Small>Level: </><Level>%d</>\n"
			"<Small>ManaCost: </><ManaCost>%.1f</>\n"
			"<Small>Cooldown: </><Cooldown>%.1f</>\n\n"
			"<Default>Launches %d bolts of fire, exploding on impact and dealing: </>"
			"<Damage>%d</>"
			"<Default> fire damage with a chance to burn</>"),
		Level, ManaCost, Cooldown, FMath::Min(Level, NumProjectiles), ScaleDamage);
}

void UAuraFireBolt::SpawnProjectiles(const FVector& ProjectileTargetLocation, const FGameplayTag& SocketTag,
                                     bool bOverridePath, float PitchOverride, AActor* HomingTarget)
{
	const bool bIsServer = GetAvatarActorFromActorInfo()->HasAuthority();
	if (!bIsServer)
	{
		return;
	}
	/**
	 * GetAvatarActorFromActorInfo: 返回触发此能力的物理Actor。可能为空。
	 */
	FVector SocketLocation =
		ICombatInterface::Execute_GetCombatSocketLocation(GetAvatarActorFromActorInfo(), SocketTag);
	FVector DeltaDirection = ProjectileTargetLocation - SocketLocation;
	FRotator Rotation = (DeltaDirection).Rotation();
	if (bOverridePath)
	{
		Rotation.Pitch = PitchOverride;
	}

	const FVector Forward = Rotation.Vector();
	const int32 EffectiveNumProjectiles = FMath::Min(GetAbilityLevel(), NumProjectiles);
	TArray<FRotator> Rotations = UAuraAbilitySystemLibrary::EvenlySpacedRotators(
		Forward, FVector::UpVector, ProjectileSpread, EffectiveNumProjectiles);
	for (const FRotator& Rot : Rotations)
	{
		if (UKismetSystemLibrary::IsDedicatedServer(GetWorld()))
		{
			/**
			 * 解决专用服务器客户端的发射炮弹问题。
			 */
			SocketLocation = SocketLocation + DeltaDirection.GetSafeNormal() * 60.f;
		}
		FTransform SpawnTransform;
		SpawnTransform.SetLocation(SocketLocation);
		SpawnTransform.SetRotation(Rot.Quaternion());
		AAuraProjectile* Projectile = GetWorld()->SpawnActorDeferred<AAuraProjectile>(
			ProjectileClass, SpawnTransform, GetOwningActorFromActorInfo(),
			Cast<APawn>(GetOwningActorFromActorInfo()),
			ESpawnActorCollisionHandlingMethod::AlwaysSpawn);
		Projectile->DamageEffectParams = MakeDamageEffectParamsFromClassDefaults();
		if (HomingTarget && HomingTarget->Implements<UCombatInterface>())
		{
			/**
			 * HomingTargetComponent: 我们目前正在瞄准的Target。只能在运行时设置(当投射体生成或更新时)。
			 */
			Projectile->ProjectileMovement->HomingTargetComponent = HomingTarget->GetRootComponent();
		}else
		{
			Projectile->HomingTargetSceneComponent = NewObject<USceneComponent>(USceneComponent::StaticClass());
			Projectile->HomingTargetSceneComponent->SetWorldLocation(ProjectileTargetLocation);
			Projectile->ProjectileMovement->HomingTargetComponent = Projectile->HomingTargetSceneComponent;
		}
		/**
		 * HomingAccelerationMagnitude: 我们朝向瞄准Target的加速度常量。总体速度常量仍会被限制在MaxSpeed范围内。
		 */
		Projectile->ProjectileMovement->HomingAccelerationMagnitude = FMath::FRandRange(HomingAccelerationMin, HomingAccelerationMax);
		/**
		 * bIsHomingProjectile: 如果为true，我们将加速度朝向我们的归巢节点。HomingTargetComponent必须在Projectile孵化后设置。
		 */
		Projectile->ProjectileMovement->bIsHomingProjectile = bLaunchHomingProjectiles;
		Projectile->FinishSpawning(SpawnTransform);
	}
}
